/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                        Intel License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//
//   * The name of Intel Corporation may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/

/*
 * cvsamples.cpp
 *
 * support functions for training and test samples creation.
 */

#include "cvhaartraining.h"
#include "_cvhaartraining.h"

/* if ipl.h file is included then iplWarpPerspectiveQ function
   is used for image transformation during samples creation;
   otherwise internal cvWarpPerspective function is used */

//#include <ipl.h>

#include <cv.h>
#include <highgui.h>

/* Calculates coefficients of perspective transformation
 * which maps <quad> into rectangle ((0,0), (w,0), (w,h), (h,0)):
 *
 *      c00*xi + c01*yi + c02
 * ui = ---------------------
 *      c20*xi + c21*yi + c22
 *
 *      c10*xi + c11*yi + c12
 * vi = ---------------------
 *      c20*xi + c21*yi + c22
 *
 * Coefficients are calculated by solving linear system:
 * / x0 y0  1  0  0  0 -x0*u0 -y0*u0 \ /c00\ /u0\
 * | x1 y1  1  0  0  0 -x1*u1 -y1*u1 | |c01| |u1|
 * | x2 y2  1  0  0  0 -x2*u2 -y2*u2 | |c02| |u2|
 * | x3 y3  1  0  0  0 -x3*u3 -y3*u3 |.|c10|=|u3|,
 * |  0  0  0 x0 y0  1 -x0*v0 -y0*v0 | |c11| |v0|
 * |  0  0  0 x1 y1  1 -x1*v1 -y1*v1 | |c12| |v1|
 * |  0  0  0 x2 y2  1 -x2*v2 -y2*v2 | |c20| |v2|
 * \  0  0  0 x3 y3  1 -x3*v3 -y3*v3 / \c21/ \v3/
 *
 * where:
 *   (xi, yi) = (quad[i][0], quad[i][1])
 *        cij - coeffs[i][j], coeffs[2][2] = 1
 *   (ui, vi) - rectangle vertices
 */
void cvGetPerspectiveTransform( CvSize src_size, double quad[4][2],
								double coeffs[3][3] ) {
	//CV_FUNCNAME( "cvWarpPerspective" );

	__BEGIN__;

	double a[8][8];
	double b[8];

	CvMat A = cvMat( 8, 8, CV_64FC1, a );
	CvMat B = cvMat( 8, 1, CV_64FC1, b );
	CvMat X = cvMat( 8, 1, CV_64FC1, coeffs );

	int i;
	for ( i = 0; i < 4; ++i ) {
		a[i][0] = quad[i][0]; a[i][1] = quad[i][1]; a[i][2] = 1;
		a[i][3] = a[i][4] = a[i][5] = a[i][6] = a[i][7] = 0;
		b[i] = 0;
	}
	for ( i = 4; i < 8; ++i ) {
		a[i][3] = quad[i-4][0]; a[i][4] = quad[i-4][1]; a[i][5] = 1;
		a[i][0] = a[i][1] = a[i][2] = a[i][6] = a[i][7] = 0;
		b[i] = 0;
	}

	int u = src_size.width - 1;
	int v = src_size.height - 1;

	a[1][6] = -quad[1][0] * u; a[1][7] = -quad[1][1] * u;
	a[2][6] = -quad[2][0] * u; a[2][7] = -quad[2][1] * u;
	b[1] = b[2] = u;

	a[6][6] = -quad[2][0] * v; a[6][7] = -quad[2][1] * v;
	a[7][6] = -quad[3][0] * v; a[7][7] = -quad[3][1] * v;
	b[6] = b[7] = v;

	cvSolve( &A, &B, &X );

	coeffs[2][2] = 1;

	__END__;
}

/* Warps source into destination by a perspective transform */
void cvWarpPerspective( CvArr* src, CvArr* dst, double quad[4][2] ) {
	CV_FUNCNAME( "cvWarpPerspective" );

	__BEGIN__;

#ifdef __IPL_H__
	IplImage src_stub, dst_stub;
	IplImage* src_img;
	IplImage* dst_img;
	CV_CALL( src_img = cvGetImage( src, &src_stub ) );
	CV_CALL( dst_img = cvGetImage( dst, &dst_stub ) );
	iplWarpPerspectiveQ( src_img, dst_img, quad, IPL_WARP_R_TO_Q,
						 IPL_INTER_CUBIC | IPL_SMOOTH_EDGE );
#else

	int fill_value = 0;

	double c[3][3]; /* transformation coefficients */
	double q[4][2]; /* rearranged quad */

	int left = 0;
	int right = 0;
	int next_right = 0;
	int next_left = 0;
	double y_min = 0;
	double y_max = 0;
	double k_left, b_left, k_right, b_right;

	uchar* src_data;
	int src_step;
	CvSize src_size;

	uchar* dst_data;
	int dst_step;
	CvSize dst_size;

	double d = 0;
	int direction = 0;
	int i;

	if ( !src || (!CV_IS_IMAGE( src ) && !CV_IS_MAT( src )) ||
			cvGetElemType( src ) != CV_8UC1 ||
			cvGetDims( src ) != 2 ) {
		CV_ERROR( CV_StsBadArg,
				  "Source must be two-dimensional array of CV_8UC1 type." );
	}
	if ( !dst || (!CV_IS_IMAGE( dst ) && !CV_IS_MAT( dst )) ||
			cvGetElemType( dst ) != CV_8UC1 ||
			cvGetDims( dst ) != 2 ) {
		CV_ERROR( CV_StsBadArg,
				  "Destination must be two-dimensional array of CV_8UC1 type." );
	}

	CV_CALL( cvGetRawData( src, &src_data, &src_step, &src_size ) );
	CV_CALL( cvGetRawData( dst, &dst_data, &dst_step, &dst_size ) );

	CV_CALL( cvGetPerspectiveTransform( src_size, quad, c ) );

	/* if direction > 0 then vertices in quad follow in a CW direction,
	   otherwise they follow in a CCW direction */
	direction = 0;
	for ( i = 0; i < 4; ++i ) {
		int ni = i + 1; if ( ni == 4 ) { ni = 0; }
		int pi = i - 1; if ( pi == -1 ) { pi = 3; }

		d = (quad[i][0] - quad[pi][0]) * (quad[ni][1] - quad[i][1]) -
			(quad[i][1] - quad[pi][1]) * (quad[ni][0] - quad[i][0]);
		int cur_direction = CV_SIGN(d);
		if ( direction == 0 ) {
			direction = cur_direction;
		} else if ( direction * cur_direction < 0 ) {
			direction = 0;
			break;
		}
	}
	if ( direction == 0 ) {
		CV_ERROR( CV_StsBadArg, "Quadrangle is nonconvex or degenerated." );
	}

	/* <left> is the index of the topmost quad vertice
	   if there are two such vertices <left> is the leftmost one */
	left = 0;
	for ( i = 1; i < 4; ++i ) {
		if ( (quad[i][1] < quad[left][1]) ||
				((quad[i][1] == quad[left][1]) && (quad[i][0] < quad[left][0])) ) {
			left = i;
		}
	}
	/* rearrange <quad> vertices in such way that they follow in a CW
	   direction and the first vertice is the topmost one and put them
	   into <q> */
	if ( direction > 0 ) {
		for ( i = left; i < 4; ++i ) {
			q[i-left][0] = quad[i][0];
			q[i-left][1] = quad[i][1];
		}
		for ( i = 0; i < left; ++i ) {
			q[4-left+i][0] = quad[i][0];
			q[4-left+i][1] = quad[i][1];
		}
	} else {
		for ( i = left; i >= 0; --i ) {
			q[left-i][0] = quad[i][0];
			q[left-i][1] = quad[i][1];
		}
		for ( i = 3; i > left; --i ) {
			q[4+left-i][0] = quad[i][0];
			q[4+left-i][1] = quad[i][1];
		}
	}

	left = right = 0;
	/* if there are two topmost points, <right> is the index of the rightmost one
	   otherwise <right> */
	if ( q[left][1] == q[left+1][1] ) {
		right = 1;
	}

	/* <next_left> follows <left> in a CCW direction */
	next_left = 3;
	/* <next_right> follows <right> in a CW direction */
	next_right = right + 1;

	/* subtraction of 1 prevents skipping of the first row */
	y_min = q[left][1] - 1;

	/* left edge equation: y = k_left * x + b_left */
	k_left = (q[left][0] - q[next_left][0]) /
			 (q[left][1] - q[next_left][1]);
	b_left = (q[left][1] * q[next_left][0] -
			  q[left][0] * q[next_left][1]) /
			 (q[left][1] - q[next_left][1]);

	/* right edge equation: y = k_right * x + b_right */
	k_right = (q[right][0] - q[next_right][0]) /
			  (q[right][1] - q[next_right][1]);
	b_right = (q[right][1] * q[next_right][0] -
			   q[right][0] * q[next_right][1]) /
			  (q[right][1] - q[next_right][1]);

	for (;;) {
		int x, y;

		y_max = MIN( q[next_left][1], q[next_right][1] );

		int iy_min = MAX( cvRound(y_min), 0 ) + 1;
		int iy_max = MIN( cvRound(y_max), dst_size.height - 1 );

		double x_min = k_left * iy_min + b_left;
		double x_max = k_right * iy_min + b_right;

		/* walk through the destination quadrangle row by row */
		for ( y = iy_min; y <= iy_max; ++y ) {
			int ix_min = MAX( cvRound( x_min ), 0 );
			int ix_max = MIN( cvRound( x_max ), dst_size.width - 1 );

			for ( x = ix_min; x <= ix_max; ++x ) {
				/* calculate coordinates of the corresponding source array point */
				double div = (c[2][0] * x + c[2][1] * y + c[2][2]);
				double src_x = (c[0][0] * x + c[0][1] * y + c[0][2]) / div;
				double src_y = (c[1][0] * x + c[1][1] * y + c[1][2]) / div;

				int isrc_x = cvFloor( src_x );
				int isrc_y = cvFloor( src_y );
				double delta_x = src_x - isrc_x;
				double delta_y = src_y - isrc_y;

				uchar* s = src_data + isrc_y * src_step + isrc_x;

				int i00, i10, i01, i11;
				i00 = i10 = i01 = i11 = (int) fill_value;

				double i = fill_value;

				/* linear interpolation using 2x2 neighborhood */
				if ( isrc_x >= 0 && isrc_x <= src_size.width &&
						isrc_y >= 0 && isrc_y <= src_size.height ) {
					i00 = s[0];
				}
				if ( isrc_x >= -1 && isrc_x < src_size.width &&
						isrc_y >= 0 && isrc_y <= src_size.height ) {
					i10 = s[1];
				}
				if ( isrc_x >= 0 && isrc_x <= src_size.width &&
						isrc_y >= -1 && isrc_y < src_size.height ) {
					i01 = s[src_step];
				}
				if ( isrc_x >= -1 && isrc_x < src_size.width &&
						isrc_y >= -1 && isrc_y < src_size.height ) {
					i11 = s[src_step+1];
				}

				double i0 = i00 + (i10 - i00) * delta_x;
				double i1 = i01 + (i11 - i01) * delta_x;
				i = i0 + (i1 - i0) * delta_y;

				((uchar*)(dst_data + y * dst_step))[x] = (uchar) i;
			}
			x_min += k_left;
			x_max += k_right;
		}

		if ( (next_left == next_right) ||
				(next_left + 1 == next_right && q[next_left][1] == q[next_right][1]) ) {
			break;
		}

		if ( y_max == q[next_left][1] ) {
			left = next_left;
			next_left = left - 1;

			k_left = (q[left][0] - q[next_left][0]) /
					 (q[left][1] - q[next_left][1]);
			b_left = (q[left][1] * q[next_left][0] -
					  q[left][0] * q[next_left][1]) /
					 (q[left][1] - q[next_left][1]);
		}
		if ( y_max == q[next_right][1] ) {
			right = next_right;
			next_right = right + 1;

			k_right = (q[right][0] - q[next_right][0]) /
					  (q[right][1] - q[next_right][1]);
			b_right = (q[right][1] * q[next_right][0] -
					   q[right][0] * q[next_right][1]) /
					  (q[right][1] - q[next_right][1]);
		}
		y_min = y_max;
	}
#endif /* #ifndef __IPL_H__ */

	__END__;
}

static
void icvRandomQuad( int width, int height, double quad[4][2],
					double maxxangle,
					double maxyangle,
					double maxzangle ) {
	double distfactor = 3.0;
	double distfactor2 = 1.0;

	double halfw, halfh;
	int i;

	double rotVectData[3];
	double vectData[3];
	double rotMatData[9];

	CvMat rotVect;
	CvMat rotMat;
	CvMat vect;

	double d;

	rotVect = cvMat( 3, 1, CV_64FC1, &rotVectData[0] );
	rotMat = cvMat( 3, 3, CV_64FC1, &rotMatData[0] );
	vect = cvMat( 3, 1, CV_64FC1, &vectData[0] );

	rotVectData[0] = maxxangle * (2.0 * rand() / RAND_MAX - 1.0);
	rotVectData[1] = ( maxyangle - fabs( rotVectData[0] ) )
					 * (2.0 * rand() / RAND_MAX - 1.0);
	rotVectData[2] = maxzangle * (2.0 * rand() / RAND_MAX - 1.0);
	d = (distfactor + distfactor2 * (2.0 * rand() / RAND_MAX - 1.0)) * width;

	/*
	    rotVectData[0] = maxxangle;
	    rotVectData[1] = maxyangle;
	    rotVectData[2] = maxzangle;

	    d = distfactor * width;
	*/

	cvRodrigues2( &rotMat, &rotVect );

	halfw = 0.5 * width;
	halfh = 0.5 * height;

	quad[0][0] = -halfw;
	quad[0][1] = -halfh;
	quad[1][0] =  halfw;
	quad[1][1] = -halfh;
	quad[2][0] =  halfw;
	quad[2][1] =  halfh;
	quad[3][0] = -halfw;
	quad[3][1] =  halfh;

	for ( i = 0; i < 4; i++ ) {
		rotVectData[0] = quad[i][0];
		rotVectData[1] = quad[i][1];
		rotVectData[2] = 0.0;
		cvMatMulAdd( &rotMat, &rotVect, 0, &vect );
		quad[i][0] = vectData[0] * d / (d + vectData[2]) + halfw;
		quad[i][1] = vectData[1] * d / (d + vectData[2]) + halfh;

		/*
		quad[i][0] += halfw;
		quad[i][1] += halfh;
		*/
	}
}


int icvStartSampleDistortion( const char* imgfilename, int bgcolor, int bgthreshold,
							  CvSampleDistortionData* data ) {
	memset( data, 0, sizeof( *data ) );
	data->src = cvLoadImage( imgfilename, 0 );
	if ( data->src != NULL && data->src->nChannels == 1
			&& data->src->depth == IPL_DEPTH_8U ) {
		int r, c;
		uchar* pmask;
		uchar* psrc;
		uchar* perode;
		uchar* pdilate;
		uchar dd, de;

		data->dx = data->src->width / 2;
		data->dy = data->src->height / 2;
		data->bgcolor = bgcolor;

		data->mask = cvCloneImage( data->src );
		data->erode = cvCloneImage( data->src );
		data->dilate = cvCloneImage( data->src );

		/* make mask image */
		for ( r = 0; r < data->mask->height; r++ ) {
			for ( c = 0; c < data->mask->width; c++ ) {
				pmask = ( (uchar*) (data->mask->imageData + r * data->mask->widthStep)
						  + c );
				if ( bgcolor - bgthreshold <= (int) (*pmask) &&
						(int) (*pmask) <= bgcolor + bgthreshold ) {
					*pmask = (uchar) 0;
				} else {
					*pmask = (uchar) 255;
				}
			}
		}

		/* extend borders of source image */
		cvErode( data->src, data->erode, 0, 1 );
		cvDilate( data->src, data->dilate, 0, 1 );
		for ( r = 0; r < data->mask->height; r++ ) {
			for ( c = 0; c < data->mask->width; c++ ) {
				pmask = ( (uchar*) (data->mask->imageData + r * data->mask->widthStep)
						  + c );
				if ( (*pmask) == 0 ) {
					psrc = ( (uchar*) (data->src->imageData + r * data->src->widthStep)
							 + c );
					perode =
						( (uchar*) (data->erode->imageData + r * data->erode->widthStep)
						  + c );
					pdilate =
						( (uchar*)(data->dilate->imageData + r * data->dilate->widthStep)
						  + c );
					de = (uchar)(bgcolor - (*perode));
					dd = (uchar)((*pdilate) - bgcolor);
					if ( de >= dd && de > bgthreshold ) {
						(*psrc) = (*perode);
					}
					if ( dd > de && dd > bgthreshold ) {
						(*psrc) = (*pdilate);
					}
				}
			}
		}

		data->img = cvCreateImage( cvSize( data->src->width + 2 * data->dx,
										   data->src->height + 2 * data->dy ),
								   IPL_DEPTH_8U, 1 );
		data->maskimg = cvCloneImage( data->img );

		return 1;
	}

	return 0;
}

void icvPlaceDistortedSample( CvArr* background,
							  int inverse, int maxintensitydev,
							  double maxxangle, double maxyangle, double maxzangle,
							  int inscribe, double maxshiftf, double maxscalef,
							  CvSampleDistortionData* data ) {
	double quad[4][2];
	int r, c;
	uchar* pimg;
	uchar* pbg;
	uchar* palpha;
	uchar chartmp;
	int forecolordev;
	float scale;
	IplImage* img;
	IplImage* maskimg;
	CvMat  stub;
	CvMat* bgimg;

	CvRect cr;
	CvRect roi;

	double xshift, yshift, randscale;

	icvRandomQuad( data->src->width, data->src->height, quad,
				   maxxangle, maxyangle, maxzangle );
	quad[0][0] += (double) data->dx;
	quad[0][1] += (double) data->dy;
	quad[1][0] += (double) data->dx;
	quad[1][1] += (double) data->dy;
	quad[2][0] += (double) data->dx;
	quad[2][1] += (double) data->dy;
	quad[3][0] += (double) data->dx;
	quad[3][1] += (double) data->dy;

	cvSet( data->img, cvScalar( data->bgcolor ) );
	cvSet( data->maskimg, cvScalar( 0.0 ) );

	cvWarpPerspective( data->src, data->img, quad );
	cvWarpPerspective( data->mask, data->maskimg, quad );

	cvSmooth( data->maskimg, data->maskimg, CV_GAUSSIAN, 3, 3 );

	bgimg = cvGetMat( background, &stub );

	cr.x = data->dx;
	cr.y = data->dy;
	cr.width = data->src->width;
	cr.height = data->src->height;

	if ( inscribe ) {
		/* quad's circumscribing rectangle */
		cr.x = (int) MIN( quad[0][0], quad[3][0] );
		cr.y = (int) MIN( quad[0][1], quad[1][1] );
		cr.width  = (int) (MAX( quad[1][0], quad[2][0] ) + 0.5F ) - cr.x;
		cr.height = (int) (MAX( quad[2][1], quad[3][1] ) + 0.5F ) - cr.y;
	}

	xshift = maxshiftf * rand() / RAND_MAX;
	yshift = maxshiftf * rand() / RAND_MAX;

	cr.x -= (int) ( xshift * cr.width  );
	cr.y -= (int) ( yshift * cr.height );
	cr.width  = (int) ((1.0 + maxshiftf) * cr.width );
	cr.height = (int) ((1.0 + maxshiftf) * cr.height);

	randscale = maxscalef * rand() / RAND_MAX;
	cr.x -= (int) ( 0.5 * randscale * cr.width  );
	cr.y -= (int) ( 0.5 * randscale * cr.height );
	cr.width  = (int) ((1.0 + randscale) * cr.width );
	cr.height = (int) ((1.0 + randscale) * cr.height);

	scale = MAX( ((float) cr.width) / bgimg->cols, ((float) cr.height) / bgimg->rows );

	roi.x = (int) (-0.5F * (scale * bgimg->cols - cr.width) + cr.x);
	roi.y = (int) (-0.5F * (scale * bgimg->rows - cr.height) + cr.y);
	roi.width  = (int) (scale * bgimg->cols);
	roi.height = (int) (scale * bgimg->rows);

	img = cvCreateImage( cvSize( bgimg->cols, bgimg->rows ), IPL_DEPTH_8U, 1 );
	maskimg = cvCreateImage( cvSize( bgimg->cols, bgimg->rows ), IPL_DEPTH_8U, 1 );

	cvSetImageROI( data->img, roi );
	cvResize( data->img, img );
	cvResetImageROI( data->img );
	cvSetImageROI( data->maskimg, roi );
	cvResize( data->maskimg, maskimg );
	cvResetImageROI( data->maskimg );

	forecolordev = (int) (maxintensitydev * (2.0 * rand() / RAND_MAX - 1.0));

	for ( r = 0; r < img->height; r++ ) {
		for ( c = 0; c < img->width; c++ ) {
			pimg = (uchar*) img->imageData + r * img->widthStep + c;
			pbg = (uchar*) bgimg->data.ptr + r * bgimg->step + c;
			palpha = (uchar*) maskimg->imageData + r * maskimg->widthStep + c;
			chartmp = (uchar) MAX( 0, MIN( 255, forecolordev + (*pimg) ) );
			if ( inverse ) {
				chartmp ^= 0xFF;
			}
			*pbg = (uchar) (( chartmp * (*palpha ) + (255 - (*palpha) ) * (*pbg) ) / 255);
		}
	}

	cvReleaseImage( &img );
	cvReleaseImage( &maskimg );
}

void icvEndSampleDistortion( CvSampleDistortionData* data ) {
	if ( data->src ) {
		cvReleaseImage( &data->src );
	}
	if ( data->mask ) {
		cvReleaseImage( &data->mask );
	}
	if ( data->erode ) {
		cvReleaseImage( &data->erode );
	}
	if ( data->dilate ) {
		cvReleaseImage( &data->dilate );
	}
	if ( data->img ) {
		cvReleaseImage( &data->img );
	}
	if ( data->maskimg ) {
		cvReleaseImage( &data->maskimg );
	}
}

void icvWriteVecHeader( FILE* file, int count, int width, int height ) {
	int vecsize;
	short tmp;

	/* number of samples */
	fwrite( &count, sizeof( count ), 1, file );
	/* vector size */
	vecsize = width * height;
	fwrite( &vecsize, sizeof( vecsize ), 1, file );
	/* min/max values */
	tmp = 0;
	fwrite( &tmp, sizeof( tmp ), 1, file );
	fwrite( &tmp, sizeof( tmp ), 1, file );
}

void icvWriteVecSample( FILE* file, CvArr* sample ) {
	CvMat* mat, stub;
	int r, c;
	short tmp;
	uchar chartmp;

	mat = cvGetMat( sample, &stub );
	chartmp = 0;
	fwrite( &chartmp, sizeof( chartmp ), 1, file );
	for ( r = 0; r < mat->rows; r++ ) {
		for ( c = 0; c < mat->cols; c++ ) {
			tmp = (short) (CV_MAT_ELEM( *mat, uchar, r, c ));
			fwrite( &tmp, sizeof( tmp ), 1, file );
		}
	}
}


int cvCreateTrainingSamplesFromInfo( const char* infoname, const char* vecfilename,
									 int num,
									 int showsamples,
									 int winwidth, int winheight ) {
	char fullname[PATH_MAX];
	char* filename;

	FILE* info;
	FILE* vec;
	IplImage* src = 0;
	IplImage* sample;
	int line;
	int error;
	int i;
	int x, y, width, height;
	int total;

	assert( infoname != NULL );
	assert( vecfilename != NULL );

	total = 0;
	if ( !icvMkDir( vecfilename ) ) {

#if CV_VERBOSE
		fprintf( stderr, "Unable to create directory hierarchy: %s\n", vecfilename );
#endif /* CV_VERBOSE */

		return total;
	}

	info = fopen( infoname, "r" );
	if ( info == NULL ) {

#if CV_VERBOSE
		fprintf( stderr, "Unable to open file: %s\n", infoname );
#endif /* CV_VERBOSE */

		return total;
	}

	vec = fopen( vecfilename, "wb" );
	if ( vec == NULL ) {

#if CV_VERBOSE
		fprintf( stderr, "Unable to open file: %s\n", vecfilename );
#endif /* CV_VERBOSE */

		fclose( info );

		return total;
	}

	sample = cvCreateImage( cvSize( winwidth, winheight ), IPL_DEPTH_8U, 1 );

	icvWriteVecHeader( vec, num, sample->width, sample->height );

	if ( showsamples ) {
		cvNamedWindow( "Sample", CV_WINDOW_AUTOSIZE );
	}

	strcpy( fullname, infoname );
	filename = strrchr( fullname, '\\' );
	if ( filename == NULL ) {
		filename = strrchr( fullname, '/' );
	}
	if ( filename == NULL ) {
		filename = fullname;
	} else {
		filename++;
	}

	for ( line = 1, error = 0, total = 0; total < num ; line++ ) {
		int count;

		error = ( fscanf( info, "%s %d", filename, &count ) != 2 );
		if ( !error ) {
			src = cvLoadImage( fullname, 0 );
			error = ( src == NULL );
			if ( error ) {

#if CV_VERBOSE
				fprintf( stderr, "Unable to open image: %s\n", fullname );
#endif /* CV_VERBOSE */

			}
		}
		for ( i = 0; (i < count) && (total < num); i++, total++ ) {
			error = ( fscanf( info, "%d %d %d %d", &x, &y, &width, &height ) != 4 );
			if ( error ) { break; }
			cvSetImageROI( src, cvRect( x, y, width, height ) );
			cvResize( src, sample, width >= sample->width &&
					  height >= sample->height ? CV_INTER_AREA : CV_INTER_LINEAR );

			if ( showsamples ) {
				cvShowImage( "Sample", sample );
				if ( cvWaitKey( 0 ) == 27 ) {
					showsamples = 0;
				}
			}
			icvWriteVecSample( vec, sample );
		}

		if ( src ) {
			cvReleaseImage( &src );
		}

		if ( error ) {

#if CV_VERBOSE
			fprintf( stderr, "%s(%d) : parse error", infoname, line );
#endif /* CV_VERBOSE */

			break;
		}
	}

	if ( sample ) {
		cvReleaseImage( &sample );
	}

	fclose( vec );
	fclose( info );

	return total;
}


void cvShowVecSamples( const char* filename, int winwidth, int winheight,
					   double scale ) {
	CvVecFile file;
	short tmp;
	int i;
	CvMat* sample;

	tmp = 0;
	file.input = fopen( filename, "rb" );

	if ( file.input != NULL ) {
		fread( &file.count, sizeof( file.count ), 1, file.input );
		fread( &file.vecsize, sizeof( file.vecsize ), 1, file.input );
		fread( &tmp, sizeof( tmp ), 1, file.input );
		fread( &tmp, sizeof( tmp ), 1, file.input );

		if ( file.vecsize != winwidth * winheight ) {
			int guessed_w = 0;
			int guessed_h = 0;

			fprintf( stderr, "Warning: specified sample width=%d and height=%d "
					 "does not correspond to .vec file vector size=%d.\n",
					 winwidth, winheight, file.vecsize );
			if ( file.vecsize > 0 ) {
				guessed_w = cvFloor( sqrt( (float) file.vecsize ) );
				if ( guessed_w > 0 ) {
					guessed_h = file.vecsize / guessed_w;
				}
			}

			if ( guessed_w <= 0 || guessed_h <= 0 || guessed_w* guessed_h != file.vecsize) {
				fprintf( stderr, "Error: failed to guess sample width and height\n" );
				fclose( file.input );

				return;
			} else {
				winwidth = guessed_w;
				winheight = guessed_h;
				fprintf( stderr, "Guessed width=%d, guessed height=%d\n",
						 winwidth, winheight );
			}
		}

		if ( !feof( file.input ) && scale > 0 ) {
			CvMat* scaled_sample = 0;

			file.last = 0;
			file.vector = (short*) cvAlloc( sizeof( *file.vector ) * file.vecsize );
			sample = scaled_sample = cvCreateMat( winheight, winwidth, CV_8UC1 );
			if ( scale != 1.0 ) {
				scaled_sample = cvCreateMat( MAX( 1, cvCeil( scale * winheight ) ),
											 MAX( 1, cvCeil( scale * winwidth ) ),
											 CV_8UC1 );
			}
			cvNamedWindow( "Sample", CV_WINDOW_AUTOSIZE );
			for ( i = 0; i < file.count; i++ ) {
				icvGetHaarTraininDataFromVecCallback( sample, &file );
				if ( scale != 1.0 ) { cvResize( sample, scaled_sample, CV_INTER_LINEAR); }
				cvShowImage( "Sample", scaled_sample );
				if ( cvWaitKey( 0 ) == 27 ) { break; }
			}
			if ( scaled_sample && scaled_sample != sample ) { cvReleaseMat( &scaled_sample ); }
			cvReleaseMat( &sample );
			cvFree( &file.vector );
		}
		fclose( file.input );
	}
}


/* End of file. */
